using System;
using System.Collections.Generic;
using System.Text;

namespace Colman.Rpg
{
    /// <summary>
    /// Represents a player's character in the game.
    /// </summary>
    [Serializable]
    public class Player : Character
    {
        /// <summary>
        /// Creates a new instance of <see cref="Player" />.
        /// </summary>
        /// <param name="name">The player's name.</param>
        /// <param name="hp">The current health points the player has.</param>
        /// <param name="ap">The base attack points the player has.</param>
        /// <param name="dp">The base defense points the player has.</param>
        /// <param name="maxHP">The maximum health points the player has.</param>
        /// <param name="experience">The experience gained by the player.</param>
        /// <param name="level">The current level the player is at.</param>
        /// <param name="gold">The amount of gold the player has.</param>
        /// <param name="weapon">The weapon the player wields.</param>
        /// <param name="armor">The armor the player wears.</param>
        public Player(string name, int hp, int ap, int dp, int maxHP, int experience, int level, int gold, Weapon weapon, Armor armor)
            : base(name, hp, maxHP)
        {
            this.weapon = weapon;
            this.armor = armor;
            this.level = level;
            base.AP = ap;
            base.DP = dp;
            this.experience = experience;
            this.gold = gold;
        }

        #region Weapon Weapon { get; set; } (weapon)
        private Weapon weapon;

        /// <summary>
        /// Gets or sets the weapon the player yields.
        /// </summary>
        public Weapon Weapon
        {
            [System.Diagnostics.DebuggerStepThrough]
            get
            {
                return this.weapon;
            }
            [System.Diagnostics.DebuggerStepThrough]
            set
            {
                if (this.weapon != value)
                {
                    this.weapon = value;
                }
            }
        }
        #endregion

        #region Armor Armor { get; set; } (armor)
        private Armor armor;

        /// <summary>
        /// Gets or sets the armor the player wears.
        /// </summary>
        public Armor Armor
        {
            [System.Diagnostics.DebuggerStepThrough]
            get
            {
                return this.armor;
            }
            [System.Diagnostics.DebuggerStepThrough]
            set
            {
                if (this.armor != value)
                {
                    this.armor = value;
                }
            }
        }
        #endregion

        #region List<Item> Items { get; } (items)
        private List<Item> items = new List<Item>();

        /// <summary>
        /// Gets the items the player has.
        /// </summary>
        public List<Item> Items
        {
            get
            {
                return this.items;
            }
        }
        #endregion

        #region int XP { get; } (experience)
        private int experience;

        /// <summary>
        /// Gets the player's experience points.
        /// </summary>
        public int XP
        {
            [System.Diagnostics.DebuggerStepThrough]
            get
            {
                return this.experience;
            }
        }
        #endregion

        #region int Level { get; } (level)
        private int level = 1;

        /// <summary>
        /// Gets the player's level.
        /// </summary>
        public int Level
        {
            [System.Diagnostics.DebuggerStepThrough]
            get
            {
                return this.level;
            }
        }
        #endregion

        #region int Gold { get; set; } (gold)
        private int gold;

        /// <summary>
        /// Gets or sets the gold that the player owns.
        /// </summary>
        public int Gold
        {
            [System.Diagnostics.DebuggerStepThrough]
            get
            {
                return this.gold;
            }
            [System.Diagnostics.DebuggerStepThrough]
            set
            {
                if (this.gold != value)
                {
                    this.gold = value;
                }
            }
        }
        #endregion

        #region int AbilityPointsOwed { get; set; } (pointsOwed)
        private int pointsOwed;

        /// <summary>
        /// Gets or sets the number of points owed to the player when levelling up.
        /// </summary>
        [System.ComponentModel.Description("Indicates the number of points owed to the player when levelling up.")]
        public int AbilityPointsOwed
        {
            [System.Diagnostics.DebuggerStepThrough]
            get
            {
                return this.pointsOwed;
            }
            [System.Diagnostics.DebuggerStepThrough]
            set
            {
                if (this.pointsOwed != value)
                {
                    this.pointsOwed = value;
                }
            }
        }
        #endregion

        /// <summary>
        /// Gets the combined AP for the player, including the weapon and all bonuses.
        /// </summary>
        public override int CombinedAP
        {
            get
            {
                int sum = base.CombinedAP;

                if (this.weapon != null)
                    sum += this.weapon.AttackPoints;

                foreach (Item item in this.items)
                {
                    if (item is MagicItem)
                        sum += item.AttackPoints;
                }

                return sum;
            }
        }

        /// <summary>
        /// Gets the combined DP for the player, including the armor and all bonuses.
        /// </summary>
        public override int CombinedDP
        {
            get
            {
                int sum = base.CombinedDP;

                if (this.armor != null)
                    sum += this.armor.DefensePoints;

                foreach (Item item in this.items)
                {
                    if (item is MagicItem)
                        sum += item.DefensePoints;
                }

                return sum;
            }
        }

        /// <summary>
        /// Gets the amount of experience remaining to get to the next level.
        /// </summary>
        public int ExperienceRemainingForNextLevel
        {
            get
            {
                return (int)(100 * Math.Pow(2, level - 1) - this.experience);
            }
        }

        /// <summary>
        /// Calculates, sets and returns the amount of XP gained by defeating a foe.
        /// </summary>
        /// <param name="character">The foe vanquished.</param>
        /// <returns>The amount of XP gained.</returns>
        public int WinAgainst(Character character)
        {
            this.experience += character.MaxHP;
            
            // Check for level up.
            if (this.ExperienceRemainingForNextLevel <= 0)
            {
                this.level++;

                this.MaxHP = (int)(this.MaxHP * 1.7); // It will take you a little longer to level up again.
            }

            return character.MaxHP;
        }

        /// <summary>
        /// Calculates, sets and returns the amount of XP lost after being defeated by a foe.
        /// </summary>
        /// <returns>The amount of XP lost.</returns>
        public void LoseBattle()
        {
            this.experience -= this.MaxHP;

            if (this.experience < 0)
            {
                this.experience = 0;
            }
        }

        /// <summary>
        /// Calculates, sets and returns the amount of XP lost after fleeing a battle.
        /// </summary>
        /// <returns>The amount of XP lost.</returns>
        public void FleeBattle()
        {
            this.experience -= this.MaxHP / 2;

            if (this.experience < 0)
            {
                this.experience = 0;
            }
        }

        #region bool IsInBattile { get; set; } (isInBattle)
        private bool isInBattle;

        /// <summary>
        /// Gets or sets the gold that the player owns.
        /// </summary>
        public bool IsInBattile
        {
            [System.Diagnostics.DebuggerStepThrough]
            get
            {
                return this.isInBattle;
            }
            [System.Diagnostics.DebuggerStepThrough]
            set
            {
                if (this.isInBattle != value)
                {
                    this.isInBattle = value;
                }
            }
        }
        #endregion
    }
}
